import { BaseModel } from '../plugin';

export default class Floor extends BaseModel {
  constructor(options) {
    super();

    this.options = {
      size: 100,
      divisions: 20,
      gridSplit: 10,
      ...options,
    };

    this.#init();
  }

  #init() {
    const grid = this.#createGrid(0x144048, 0x57fff7);
    this.object.add(grid);

    const wave = this.#createWave(0x09533d, 0x2dac86);
    wave.position.z = -0.1;
    this.object.add(wave);
  }

  #createGrid(gridColor, crossColor) {
    const { size, divisions } = this.options;
    const gap = size / divisions;
    const total = (divisions + 1) ** 2;

    const group = new THREE.Group();

    // 网格
    const gridHelper = new THREE.GridHelper(size, divisions, gridColor, gridColor);
    gridHelper.rotation.x = Math.PI / 2;
    gridHelper.renderOrder = 1;
    group.add(gridHelper);

    // 十字
    const m = 20;
    const n = 3;
    let points = [
      [-m, n],
      [-m, -n],
      [-n, -n],
      [-n, -m],
      [n, -m],
      [n, -n],
      [m, -n],
      [m, n],
      [n, n],
      [n, m],
      [-n, m],
      [-n, n],
      [-m, n],
    ];
    points = points.map((v) => new THREE.Vector2(v[0], v[1]));

    const shape = new THREE.Shape(points);
    const geometry = new THREE.ShapeGeometry(shape);
    const material = new THREE.MeshBasicMaterial({
      color: crossColor,
      // depthTest: false,
    });

    const insMesh = new THREE.InstancedMesh(geometry, material, total);
    insMesh.name = 'insMesh';
    insMesh.renderOrder = 2;

    let transform = new THREE.Object3D();
    const shapeScale = (0.1 * gap) / 25;

    for (let half = size / 2, x = -half, index = 0; x <= half; x += gap) {
      for (let y = -half; y <= half; y += gap) {
        transform.position.set(x, y, 0);
        transform.scale.set(shapeScale, shapeScale);
        transform.updateMatrix();
        insMesh.setMatrixAt(index++, transform.matrix);
      }
    }

    group.add(insMesh);

    return group;
  }

  #createWave(color, circleColor) {
    const { size, divisions, gridSplit } = this.options;
    const num = divisions * gridSplit;

    const geometry = new THREE.PlaneGeometry(size, size, num - 1, num - 1);
    const material = new THREE.ShaderMaterial({
      uniforms: {
        uColor: {
          value: new THREE.Color(color),
        },
        uCircleColor: {
          value: new THREE.Color(circleColor),
        },
        uRadius: {
          value: 0,
        },
        uWidth: {
          value: 0.1,
        },
      },
      vertexShader: `
        varying vec2 vUv;

        void main() {
          vUv = uv;
          vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
          gl_PointSize *= ( 50.0 / - mvPosition.z );
          gl_Position = projectionMatrix * mvPosition;
        }
      `,
      fragmentShader: `
        uniform vec3 uColor;
        uniform vec3 uCircleColor;
        uniform float uRadius;
        uniform float uWidth;
        varying vec2 vUv;

        void main() {
          float h = uWidth / 2.0;
          float d = distance(vUv, vec2(0.5));
          float r = smoothstep(uRadius - h, uRadius, d) - smoothstep(uRadius, uRadius + h, d);
          vec3 c = mix(uColor, uCircleColor, r);

          gl_FragColor = vec4(c, 1.0);

          #include <colorspace_fragment>
        }
      `,
    });
    const mesh = new THREE.Points(geometry, material);

    const tween = new TWEEN.Tween(material.uniforms.uRadius, this.tweenGroup)
      .to({ value: 2 }, 10 * 1000)
      .repeat(Infinity);
    this.tweenList.push(tween);

    return mesh;
  }
}
